#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyleft 2011 Orcun Avsar <orc.avs@gmail.com>
#
# GNU General Public Licence (GPL)
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


import thread


Qt = None

def use_qt():
  """Import PyQt4 and set Qt to PyQt4 module.

  This provides importing PyQt4 only if developer needs to use
  Qt related features.
  """
  global Qt

  import PyQt4
  Qt = PyQt4


class NoEmitterIDError(Exception):

  msg = "No emitter_id property is set for object."

  def __str__(self):
    return self.msg


class Signal(object):

  callbacks = {}

  def __init__(self, emitter_id=None):
    if emitter_id:
      self.emitter_id = emitter_id

  def emit(self, signal, *args, **kwargs):
    if not hasattr(self, 'emitter_id'):
      raise NoEmitterIDError

    key = (self.emitter_id, signal)
    callbacks = self.callbacks.get(key, [])
    for callback in callbacks:
      thread.start_new_thread(callback, args, kwargs)

  def connect(self, emitter_id, signal, callback):
    key = (emitter_id, signal)
    if not key in self.callbacks:
      self.callbacks[key] = []

    callbacks = self.callbacks[key]
    if callback in callbacks:
      return
    else:
      callbacks.append(callback)


class QtSignalWrapper(Signal):

  objects = {}
  callback_wrappers = {}

  def __init__(self, *args, **kwargs):
    use_qt()
    super(QtSignalWrapper, self).__init__(*args, **kwargs)

  def connect(self, emitter_id, signal, callback):
    """Connects signal through a callback wrapper for Qt.

    This allows connecting with Qt signals in the background
    while still using easythreads type signalling for coding.

    (Object with emitter_id) -->signal--> callback_wrapper -
                                                            |
                                                            v
    callback <--- Qt signal <--- (QObject for emitter_id) <-

    """
    if not emitter_id in self.objects:
      self.objects[emitter_id] = Qt.QtCore.QObject()
    qsignal = Qt.QtCore.SIGNAL(signal)
    object = self.objects[emitter_id]
    object.connect(object, qsignal, callback)
    try:
      object.connect(emitter_id, signal, callback)
    except TypeError:
      pass

    key = (emitter_id, qsignal, callback)
    if not key in self.callback_wrappers:
      def callback_wrapper(*args, **kwargs):
        object.emit(qsignal, *args, **kwargs)
      self.callback_wrappers[key] = callback_wrapper

    callback_wrapper = self.callback_wrappers[key]
    super(QtSignalWrapper, self).connect(
      emitter_id, signal, callback_wrapper)
